/*
 * File Name:               XReseter.cs
 * 
 * Description:             普通类
 * Author:                  lisiyu <576603306@qq.com>

 * Create Date:             2017/11/11
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEngine;

public class XReseter 
{
    public static BindingFlags BINDINGFLAG = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly;

    private static Dictionary<int, TClassInfo> retMap;
    public static void toDoIt(object target)
    {
        if (retMap == null)
            retMap = new Dictionary<int, TClassInfo>(100);
        else
            retMap.Clear();

        parse_class(target, target.GetType());
        output();
    }

    private static void output()
    {
        var str_ftl = Resources.Load<TextAsset>("xreseter").text;
        foreach (var item in retMap.Values)
        {
            var template = new XTemplate(str_ftl);
            var content = parse(item, template);
            File.WriteAllText(string.Format("Assets/Code/Game/Match/Reseters/{0}Reseter.cs", item.type_name), content);
        }
    }

    private static string parse(TClassInfo info, XTemplate template)
    {
        template.setValue("#CLASS_NAME#", info.type_name);
        template.setValue("#CLASS_FULL_NAME#", info.type_fullName);
        template.beginLoop("#PROCESS#");
        foreach (var field in info.fields)
        {
            if(field.reset_type == TFieldType.BaseType)
            {
                template.setCondition("PRIVATE", false);
                template.setCondition("PUBLIC", false);
                template.setCondition("FUNCTION", false);
                template.setCondition("BASETYPE", true);
            }
            else if (field.reset_type == TFieldType.Function)
            {
                template.setCondition("PRIVATE", false);
                template.setCondition("PUBLIC", false);
                template.setCondition("FUNCTION", true);
                template.setCondition("BASETYPE", false);
            }
            else
            {
                template.setCondition("FUNCTION", false);
                template.setCondition("BASETYPE", false);

                if (field.isPrivate)
                {
                    template.setCondition("PRIVATE", true);
                    template.setCondition("PUBLIC", false);
                }
                else
                {
                    template.setCondition("PRIVATE", false);
                    template.setCondition("PUBLIC", true);
                }

                if (field.reset_type == TFieldType.Value)
                {
                    template.setCondition("VALUE", true);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if (field.reset_type == TFieldType.Null)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", true);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if (field.reset_type == TFieldType.Property)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", true);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if (field.reset_type == TFieldType.Clear)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", true);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if (field.reset_type == TFieldType.DoReset)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", true);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if (field.reset_type == TFieldType.Children_Array)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", true);
                    template.setCondition("CHILDREN_LIST", false);
                }
                else if(field.reset_type == TFieldType.Children_List)
                {
                    template.setCondition("VALUE", false);
                    template.setCondition("NULL", false);
                    template.setCondition("PROPERTY", false);
                    template.setCondition("CLEAR", false);
                    template.setCondition("CHILD", false);
                    template.setCondition("CHILDREN_ARRAY", false);
                    template.setCondition("CHILDREN_LIST", true);
                }
                else
                {
                    throw new NotImplementedException();
                }
            }

            template.setValue("#VAR_NAME#", field.field_name);
            template.setValue("#VAR_VALUE#", field.field_value);
            template.setValue("#VAR_TYPE#", field.field_type);
            template.setValue("#VAR_TYPE_FULL#", field.field_fullType);
            template.nextLoop();
        }
        template.endLoop();

        template.beginLoop("#LOOP_FIELDS#");
        foreach (var field in info.fields)
        {
            if(field.reset_type == TFieldType.Value)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", true);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN", false);
            }
            else if(field.reset_type == TFieldType.DoReset)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", true);
                template.setCondition("CHILDREN", false);
            }
            else if(field.reset_type == TFieldType.Children_Array
                || field.reset_type == TFieldType.Children_List)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN", true);
            }
            else
            {
                template.setCondition("NOTHING", false);
            }

            template.setValue("#VAR_NAME#", field.field_name);
            template.setValue("#VAR_TYPE#", field.field_type);
            template.setValue("#VAR_TYPE_FULL#", field.field_fullType);
            template.nextLoop();
        }
        template.endLoop();

        template.beginLoop("#LOOP_CONSTRUCTOR#");
        foreach (var field in info.fields)
        {
            if (field.isPrivate)
            {
                template.setCondition("PRIVATE", true);
                template.setCondition("PUBLIC", false);
            }
            else
            {
                template.setCondition("PRIVATE", false);
                template.setCondition("PUBLIC", true);
            }

            if (field.reset_type == TFieldType.Value)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", true);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN_ARRAY", false);
                template.setCondition("CHILDREN_LIST", false);
            }
            else if(field.reset_type == TFieldType.DoReset)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", true);
                template.setCondition("CHILDREN_ARRAY", false);
                template.setCondition("CHILDREN_LIST", false);
            }
            else if(field.reset_type == TFieldType.Children_Array)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN_ARRAY", true);
                template.setCondition("CHILDREN_LIST", false);
            }
            else if(field.reset_type == TFieldType.Children_List)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN_ARRAY", false);
                template.setCondition("CHILDREN_LIST", true);
            }
            else
            {
                template.setCondition("NOTHING", false);
            }

            template.setValue("#VAR_NAME#", field.field_name);
            template.setValue("#VAR_TYPE#", field.field_type);
            template.setValue("#VAR_TYPE_FULL#", field.field_fullType);
            template.nextLoop();
        }
        template.endLoop();

        template.beginLoop("#LOOP_FUNC#");
        foreach (var field in info.fields)
        {
            if (field.reset_type == TFieldType.Value)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", true);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN", false);
            }
            else if(field.reset_type == TFieldType.DoReset)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", true);
                template.setCondition("CHILDREN", false);
            }
            else if(field.reset_type == TFieldType.Children_Array
                || field.reset_type == TFieldType.Children_List)
            {
                template.setCondition("NOTHING", true);
                template.setCondition("VALUE", false);
                template.setCondition("CHILD", false);
                template.setCondition("CHILDREN", true);
            }
            else
            {
                template.setCondition("NOTHING", false);
            }

            template.setValue("#VAR_NAME#", field.field_name);
            template.setValue("#VAR_TYPE#", field.field_type);
            template.setValue("#VAR_TYPE_FULL#", field.field_fullType);
            template.nextLoop();
        }
        template.endLoop();

        return template.getContent();
    }

    private static TFieldInfo parse_field(FieldInfo field, object target)
    {
        if (field.IsInitOnly) return null;
        if (field.IsStatic) return null;
        var fieldType = field.FieldType;
        if (fieldType.IsPointer) return null;

        var _field = new TFieldInfo();
        _field.field_name = field.Name;
        _field.field_type = fieldType.Name;
        _field.field_fullType = fieldType.FullName.Replace("+", ".");
        _field.isPrivate = !field.IsPublic; // 这里要包括protecter 和 private 

        if(target == null)
        {
            _field.reset_type = TFieldType.Null;
            return _field;
        }
       
        var fieldValue = field.GetValue(target);
        var attrs = field.GetCustomAttributes(typeof(XResetAttribute), true);
        if (attrs.Length > 0)
        {
            var resetType = (attrs[0] as XResetAttribute).ResetType;
            if (resetType == XResetType.NoReset)
                return null;
            _field.reset_type = parse(resetType);
            if (_field.reset_type == TFieldType.DoReset)
            {
                parse_class(fieldValue, fieldType);
            }
            else if (_field.reset_type == TFieldType.Children_Array)
            {
                var array = (Array)fieldValue;
                var elementType = fieldType.GetElementType();
                _field.field_type = elementType.Name;
                _field.field_fullType = elementType.FullName.Replace("+", ".");
                parse_class(array.GetValue(0), elementType);
                // todo list dictionary and etc.
            }
            else if(_field.reset_type == TFieldType.Children_List)
            {
                var list = (IList)fieldValue;
                var element = list[0];
                var elementType = element.GetType();
                _field.field_type = elementType.Name;
                _field.field_fullType = elementType.FullName.Replace("+", ".");
                parse_class(element, elementType);
            }
            else if (_field.reset_type == TFieldType.Clear)
            {
                if (fieldValue is IDictionary)
                    _field.field_type = "IDictionary";
                else if (fieldValue is IList)
                    _field.field_type = "IList";
                else if (fieldValue is Array)
                    _field.field_type = "Array";
                else
                {
                    throw new NotImplementedException();
                }
            }
        }
        else if (fieldValue is int || fieldValue is float || fieldValue is long || fieldValue is uint || fieldValue is byte
            || fieldValue is XNumber || fieldValue is XVector3 || fieldValue is XQuaternion || fieldValue is XVector2)
        {
            _field.reset_type = TFieldType.Value;
            _field.field_value = parse_fieldValue(fieldValue);
        }
        else if (fieldValue is IXProperty)
        {
            _field.reset_type = TFieldType.Property;
            var _value = fieldType.GetField("_Value", BINDINGFLAG);
            _field.field_value = parse_fieldValue(_value.GetValue(fieldValue));
        }
        else
        {
            return null;
        }
        return _field;
    }

    private static void parse_class(object target, Type type)
    {
        var hash_type = type.GetHashCode();
        if (retMap.ContainsKey(hash_type))
            return;

        var ret = new TClassInfo();
        retMap.Add(hash_type, ret);

        ret.type_name = type.Name;
        ret.type_fullName = type.FullName.Replace("+", ".");
        ret.fields = new List<TFieldInfo>();

        if(type.BaseType != null && type.BaseType.Namespace != "System")
        {
            var _field = new TFieldInfo();
            _field.field_type = type.BaseType.Name;
            _field.field_fullType = type.BaseType.FullName.Replace("+", ".");
            _field.reset_type = TFieldType.BaseType;
            ret.fields.Add(_field);

            parse_class(target, type.BaseType);
        }

        var fields = type.GetFields(BINDINGFLAG);
        foreach (var field in fields)
        {
            var _field = parse_field(field, target);
            if (_field == null) continue;

            ret.fields.Add(_field);
        }
    }

    private class TClassInfo
    {
        public string type_fullName;
        public string type_name;
        public List<TFieldInfo> fields;
    }

    private class TFieldInfo
    {
        public string field_name;
        public string field_type;
        public string field_fullType;
        public string field_value;

        public TFieldType reset_type;
        public bool isPrivate;
    }

    private enum TFieldType
    {
        Null,
        Value,
        Clear,
        Children_Array,
        Children_List,
        DoReset,
        Property,
        Function,
        BaseType,
    }

    private static TFieldType parse(XResetType resetType)
    {
        var ret = TFieldType.Value;
        switch (resetType)
        {
            case XResetType.DoReset:
                ret = TFieldType.DoReset;
                break;
            case XResetType.Null:
                ret = TFieldType.Null;
                break;
            case XResetType.Clear:
                ret = TFieldType.Clear;
                break;
            case XResetType.Children_Array:
                ret = TFieldType.Children_Array;
                break;
            case XResetType.Children_List:
                ret = TFieldType.Children_List;
                break;
            case XResetType.Function:
                ret = TFieldType.Function;
                break;
            case XResetType.NoReset:
            default:
                break;
        }
        return ret;
    }

    private static string parse_fieldValue(object fieldValue)
    {
        if(fieldValue is XNumber)
        {
            return string.Format("XNumber.create_row({0})", ((XNumber)fieldValue).raw); 
        }
        else if(fieldValue is XVector3)
        {
            var value = (XVector3)fieldValue;
            return string.Format("new XVector3(XNumber.create_row({0}), XNumber.create_row({1}), XNumber.create_row({2}))", value.x.raw, value.y.raw, value.z.raw);
        }
        else if(fieldValue is XVector2)
        {
            var value = (XVector2)fieldValue;
            return string.Format("new XVector2(XNumber.create_row({0}), XNumber.create_row({1}))", value.x.raw, value.y.raw);
        }
        else if(fieldValue is XQuaternion)
        {
            var value = (XQuaternion)fieldValue;
            return string.Format("new XQuaternion(XNumber.create_row({0}), XNumber.create_row({1}), XNumber.create_row({2}), XNumber.create_row({3}))", value.x.raw, value.y.raw, value.z.raw, value.w.raw);
        }
        else if(fieldValue is XProperty<int>)
        {
            var value = (XProperty<int>)fieldValue;
            return string.Format("");
        }
        else if(fieldValue is Enum)
        {
            return string.Format("({0}){1}", fieldValue.GetType().Name, (int)fieldValue);
        }
        else
        {
            return fieldValue.ToString();
        }
    }

    public static void log(params object[] args)
    {
        var _index = (int)args[4];
        if(_index == -1)
            Debug.LogErrorFormat("检测到{0}.{1}值不相同 {2}-{3}", args[0], args[1], args[2], args[3]);
        else
            Debug.LogErrorFormat("检测到index:{4} 的 {0}.{1}值不相同 {2}-{3}", args[0], args[1], args[2], args[3], _index);
    }
}
